/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.bootstrap.template.client;

import java.util.Arrays;
import java.util.List;

import com.cleangwt.bootstrap.client.style.Styles;
import com.cleangwt.bootstrap.client.ui.Navigators;
import com.cleangwt.bootstrap.client.ui.PageNavigator;
import com.cleangwt.page.client.PageContext;
import com.cleangwt.user.client.ui.RawWidget;
import com.cleangwt.user.shared.Link;
import com.cleangwt.user.shared.Links;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Style.WhiteSpace;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.HasCloseHandlers;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.layout.client.Layout.AnimationCallback;
import com.google.gwt.layout.client.Layout.Layer;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.StackLayoutPanel;

/**
 * A {@link StackLayoutPanel} with {@link PageNavigator}s.
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public class Sidebar extends DockLayoutPanel implements HasCloseHandlers<Sidebar>,
    HasOpenHandlers<Sidebar> {

  private static final String DEFAUL_STYLENAME = "sidebar";
  private static final String TOGGLE_STYLE = "sidebar-toggle";
  private static final String ARROW_STYLE = "arrow";

  public static final int DIRECTOR_WIDTH = 280;
  public static final int TOGGLE_WIDTH = 32;
  public static final int VIEW_WIDTH = DIRECTOR_WIDTH + TOGGLE_WIDTH;
  public static final int ANIMATION_TIME = 250;

  private boolean open = false;
  private SimplePanel toggleWraper = new SimplePanel();
  private StackLayoutPanel stack = new StackLayoutPanel(Unit.PX);
  private RawWidget toggle = new RawWidget(AnchorElement.TAG);
  private List<Link> links;

  private class SidebarHandler implements ClickHandler, SelectionHandler<Integer> {

    @Override
    public void onSelection(SelectionEvent<Integer> event) {
      // for stack layout panel
      int index = event.getSelectedItem();
      Link leaf = Links.getDeafultLeaf(links.get(index));
      if (leaf != null) {
        PageContext.redirect(leaf.getValue());
      }
    }

    @Override
    public void onClick(ClickEvent event) {
      // for toggle button
      if (open) {
        close();
      } else {
        open();

      }
    }
  }

  /**
   * Creates a sidebar.
   */
  public Sidebar(List<Link> links) {
    super(Unit.PX);
    this.links = links;
    Element arrowElement = Document.get().createSpanElement();
    arrowElement.setClassName(ARROW_STYLE);

    this.addWest(stack, 0);
    this.addWest(toggleWraper, TOGGLE_WIDTH);
    this.setStyleName(DEFAUL_STYLENAME);

    // no wrap, so that the animation looks smooth
    stack.getElement().getStyle().setWhiteSpace(WhiteSpace.NOWRAP);
    toggleWraper.setWidget(toggle);
    toggle.getElement().appendChild(arrowElement);
    toggle.setStyleName(TOGGLE_STYLE);

    // init
    for (Link link : links) {
      PageNavigator navigator = new PageNavigator(Navigators.PILLS_STYLE);
      navigator.addStyleName(Navigators.STACKED_STYLE);
      if (link.getValue() != null) {
        navigator.setLinks(Arrays.asList(link));
      } else {
        navigator.setLinks(link.getSubLinks());
      }
      stack.add(navigator, link.getName(), 36);
    }

    // handler
    SidebarHandler sidebarHandler = new SidebarHandler();
    toggle.addDomHandler(sidebarHandler, ClickEvent.getType());
    stack.addSelectionHandler(sidebarHandler);

    open();
  }

  @Override
  public HandlerRegistration addCloseHandler(CloseHandler<Sidebar> handler) {
    return addHandler(handler, CloseEvent.getType());
  }

  @Override
  public HandlerRegistration addOpenHandler(OpenHandler<Sidebar> handler) {
    return addHandler(handler, OpenEvent.getType());
  }

  /**
   * Checks whether the open state.
   * 
   * @return whether the open state.
   */
  public boolean isOpen() {
    return open;
  }

  /**
   * Sets the active page.
   * 
   * @param pageName the page name
   */
  public void setActivePage(String pageName) {
    for (int i = 0; i < stack.getWidgetCount(); i++) {
      if (stack.getWidget(i) instanceof PageNavigator) {
        PageNavigator nav = (PageNavigator) stack.getWidget(i);
        nav.setActivePage(pageName);
        if (nav.isActive()) {
          stack.showWidget(i, false);
          break;
        }
      }
    }
  }

  private void close() {
    open = false;
    // slide out
    setWidgetSize(stack, 0);
    setWidgetSize(toggleWraper, 0);
    // do animation
    animate(ANIMATION_TIME, new AnimationCallback() {

      @Override
      public void onAnimationComplete() {
        // fire the close event
        CloseEvent.fire(Sidebar.this, Sidebar.this);
        // on slide out complete, slide toggle in
        setWidgetSize(toggleWraper, TOGGLE_WIDTH);
        // change toggle face
        toggle.removeStyleName(Styles.OPEN);
        // do slide out animation
        animate(ANIMATION_TIME);
      }

      @Override
      public void onLayout(Layer layer, double progress) {
      }
    });

  }

  private void open() {
    open = true;
    // slide toggle button out
    setWidgetSize(toggleWraper, 0);
    // do slide out animation
    animate(ANIMATION_TIME, new AnimationCallback() {

      @Override
      public void onAnimationComplete() {
        // fire the open event
        OpenEvent.fire(Sidebar.this, Sidebar.this);
        // on slide out complete, slide naivgator with toggle in
        setWidgetSize(stack, DIRECTOR_WIDTH);
        setWidgetSize(toggleWraper, TOGGLE_WIDTH);
        // change toggle face
        toggle.addStyleName(Styles.OPEN);
        // do slide in animation
        animate(ANIMATION_TIME);
      }

      @Override
      public void onLayout(Layer layer, double progress) {
      }
    });
  }

}
